package  
{
	import flash.display.Sprite;
	import flash.net.SharedObject;
	import flash.net.SharedObjectFlushStatus;
	import flash.text.TextField;
	import flash.utils.getTimer;
	import flash.filesystem.*;
	import flash.events.Event;

	/**
	 * ...
	 * @author patsup
	 * 
	 * 		This is basically a structure that holds data for a single session (game), persistant across rounds.
	 * 	The displaying of it is mainly just for testing purposes. It is instantiated in the PGame class, 
	 * 	so other classes (such as PreRoundMenu) need accessor functions.
	 * 		EvaluatePayoutTiers() calculates/sets the payout tiers.
	 * 		GetPayoutTable() returns a string with the info the above had calculated.
	 * 		CalcWinningsWithBetAndScore() returns how much should be won, based on currentBet and score (using those tiers)
	 * 		ApplyRoundEnd() does the necessary actions for the end of a round.
	 * 		UpdateText() is mainly just for testing purposes (i.e. when displaying this class).
	 * 
	 * Set DEBUG_MODE to false in Main.as to not display (doesn't add to display list with addChild)
	 * 
	 */
	public class SessionStats extends Sprite
	{
		//data tracking interval
		private const DATA_TRACK_INTERVAL:int = 300000;				//300000 milliseconds = 5 minutes
		
		//session stats image parameters
		private const SS_COLOR:int = 0xC0C0C0;
		private const SS_ALPHA:Number = 0.3;
		private const SS_WIDTH:int = 160;
		private const SS_HEIGHT:int = 160;
		
		//default constants / constant parameters
		private const STARTING_DEFAULT_BET:int = 5;
		private const STARTING_MONEY:int = 100;
		private const MAX_BET_SIZE_PERCENT:Number = 0.15;			//percentage of total money
		private const MIN_BET_SIZE_PERCENT:Number = 0.05;
		private const BET_INCREMENT:int = 1;
		private const NUM_PAYOUT_TIERS:int = 4;
		private const TIER_RANGE_FRACTION_OF_TOTAL:int = 15;	//1/15th of the total sum of pirates possible (the score u get on forfeit)
		
		private var totalMoney:int;								//total amount of money
		private var currentBet:int;								//this round's wager
		private var gameTime:Number;	//keeps track of how long PGame has been played (during rounds, between preround menus) in milliseconds - only gets updated at end of round
		private var roundStartTime:int;	//keeps track of when the round started (used to calculate how long the round lasted)
		private var dataLastTrackedTime:int;//track when data was last tracked
		private var trackedDataString:String;
		
		private var achievements:Achievements;					//class to hold achievements
		
		private var payoutTiers:Array = new Array();			//payout table tiers
		
		private var txtLabel:TextField;				//label
		private var gameRef:PGame;
		
		public function SessionStats(gameRef:PGame, xCoord:int, yCoord:int) 
		{
			this.gameRef = gameRef;
			x = xCoord;
			y = yCoord;
			txtLabel = new TextField();
			
			achievements = new Achievements(gameRef.getMainRef());	//it needs mainref so it can display achievements itself
			
			EvaluatePayoutTiers();		//payout tiers don't change now, so we can put this in the constructor
			InitNewGame();
		}
		
		//saves tracking data... keeps track of it as well as saves to the sharedobject
		//but only keep track/save it after a certain time...
		private function SaveTrackedData(savedData:SharedObject):void
		{
			//var s:Vector.<String> = new Vector.<String>;
			if (gameTime - dataLastTrackedTime >= DATA_TRACK_INTERVAL) {
				dataLastTrackedTime = gameTime;							//mark when this data is being tracked (has to come before the GetDataAsString() if we want to record it correctly)
				trackedDataString += achievements.GetDataAsString();	//save it to the var we're keeping track of
				savedData.data.trackedDataString = trackedDataString;	//save it to the shared object
			}
		}
		//SaveGameData()
		//tells achievements class to save itself
		public function SaveGameData(savedData:SharedObject):void
		{
			savedData.data.hasData = true;
			savedData.data.currentBet = currentBet;
			savedData.data.totalMoney = totalMoney;
			savedData.data.gameTime = gameTime;
			SaveTrackedData(savedData);							//for testing purposes, save the data we've been tracking (has to come before dataLastTrackedTime)
			savedData.data.dataLastTracked = dataLastTrackedTime;//has to come after SaveTrackedData() so we have the correct time mark of when it was tracked
			
			achievements.SaveYourself(savedData.data);			//tell achievements to save its data
			
			var _ts:Date = new Date();
			var _timeStamp:String = _ts.hours + ":" + _ts.minutes + " " + _ts.month + "/" + _ts.date + "/" + _ts.fullYear;
			//+ _ts.toLocaleTimeString();
			trace("timeStamp: " + _timeStamp);
			savedData.data.timeStamp = _timeStamp;
			
			
			
			
			if (savedData.flush() == SharedObjectFlushStatus.FLUSHED) {
				trace("Saved Data flush success!");
			} else {
				trace("Saved Data flush failed");
			}
		}
		
		//InitNewGame()
		public function InitNewGame():void
		{
			totalMoney = STARTING_MONEY;
			currentBet = STARTING_DEFAULT_BET;
			gameTime = 0;
			dataLastTrackedTime = 0;
			trackedDataString = "";
			
			achievements.InitNewGame();
			InitImage();
		}
		//InitLoadGame()
		//Sets data upon loading a new game
		public function InitLoadGame(data:Object):void
		{
			currentBet = data.currentBet;
			totalMoney = data.totalMoney;
			gameTime = data.gameTime;
			dataLastTrackedTime = data.dataLastTracked;	//for testing purposes, keep track of when you last tracked data
			trackedDataString = data.trackedDataString;	//for testing purposes, load the data we've been tracking
			
			achievements.LoadYourself(data);
			InitImage();
		}
		
		//InitImage()
		//Initializes the image of the SessionStats
		protected function InitImage():void
		{
			graphics.beginFill(SS_COLOR, SS_ALPHA);
			graphics.drawRect(0, 0, SS_WIDTH, SS_HEIGHT);
			graphics.endFill();
			
			UpdateText();
			
			//txtLabel.border = true;
			
			txtLabel.x = 0;
			txtLabel.y = 0;
			txtLabel.width = width;
			txtLabel.height = height;
			//txtLabel.width = txtLabel.textWidth*1.5;
			//txtLabel.height = txtLabel.textHeight * 1.1;
			//btnLabel.x = 0.5 * SS_WIDTH - 0.5 * btnLabel.width;
			//btnLabel.y = 0.5 * SS_HEIGHT - 0.5 * btnLabel.height;
			addChild(txtLabel);								//now add this txt label to the display list
			
		}
		
		//ApplyRoundStart()
		//called at beginning of round
		public function ApplyRoundStart():void
		{
			roundStartTime = getTimer();
			
			UpdateText();
		}
		//ApplyRoundEnd()
		//call at the end of a round to calculate stats, determine winnings, and see if you "lost" (not enough money to bet next round)
		//"LOSING" currently does nothing, but there's a check for it!
		//basic stats: currentBet, totalMoney, gameTime, dataLastTracked
		public function ApplyRoundEnd():void
		{
			totalMoney += CalcWinningsWithBetAndScore();			//adjust total money by win/lose amount
			
			//need to know how much money we won at end of round before achievements are checked
			dispatchEvent(new AchievementEvent(AchievementEvent.END_OF_ROUND, gameRef.GetRoundScore()));
			
			//only update game time AFTER end of round achievement-check has happened (to get correct timestamp)
			gameTime += GetRoundLengthInMilliseconds();
			
			if (currentBet > (MAX_BET_SIZE_PERCENT * totalMoney)) {	//if bet > maxbetsize, then reduce betsize
				currentBet = Math.floor(MAX_BET_SIZE_PERCENT * totalMoney);
			}
			if (currentBet > totalMoney) {							//if bet > money left, then reduce betsize
				currentBet = totalMoney;
			}
			if (currentBet < (MIN_BET_SIZE_PERCENT * totalMoney)) {
				trace("wow i didn't think you could lose this game but you did");		//you lose
				trace("currentBet = " + currentBet + "totalMoney = " + totalMoney + "compareto: " + (MIN_BET_SIZE_PERCENT * totalMoney));
			}
		}
		
		//GetPayoutTable()
		//returns payout table as a string so it could be displayed
		public function GetPayoutTable():String
		{
			var _pt:String;
			var _payout:int = payoutTiers[0].GetPayout();
			
			_pt = "Payout Table\nScore: " + payoutTiers[0].GetTier() + " = " + _payout + "x bet";
			for (var i:int = 1; i < payoutTiers.length; i++)
			{
				_payout = payoutTiers[i].GetPayout();
				_pt += "\nScore: " + (payoutTiers[i - 1].GetTier() + 1) + "-" + payoutTiers[i].GetTier() + " = ";
				if(_payout != 0)
					_pt += _payout + " x bet";
				else
					_pt += "breakeven";
			}
			_pt += "\nScores " + (payoutTiers[payoutTiers.length - 1].GetTier() + 1) + " and up = loss";
			return _pt;
		}
		//CalcWinningsWithBetAndScore()
		//returns the amount of money won/lost, based on your current bet and score
		public function CalcWinningsWithBetAndScore():Number
		{
			var _winnings:Number = currentBet;
			var _score:int = gameRef.GetRoundScore();
			//if (_score == 0) {
				//_winnings *= 5;
			//} else if (_score < 4) {
				//_winnings *= 3;
			//} else if (_score < 7) {
				//_winnings *= 2;
			//} else if (_score < 10) {
				//_winnings *= 1;
			//} else if (_score < 13) {
				//no change in money, i.e. you make back what you bet
				//_winnings *= 0;
			//} else {
				//_winnings *= -1;
			//}
			var i:int = 0;
			var _tierNotFound:Boolean = true;
			while (_tierNotFound && i < payoutTiers.length) {
				if (_score <= payoutTiers[i].GetTier()) {
					_winnings *= payoutTiers[i].GetPayout();
					_tierNotFound = false;
				}
				i++;
			}
			if (_tierNotFound) {
				_winnings *= -1;
			}
			return _winnings;
		}
		
		//EvaluatePayoutTiers()
		//calculate/sets the payout tiers and stores them in a payoutTiers:Array
		//based on the total number of pirates, NUM_PAYOUT_TIERS, and TIER_RANGE_FRACTION_OF_TOTAL
		private function EvaluatePayoutTiers():void
		{
			var _pirateTotalSum:int  = 0;
			var i:int;
			for (i = 1; i <= gameRef.TotalNumPirates(); i++) {
				_pirateTotalSum += i;
			}
			//default of 9 pirates means: sum=45, fraction=1/15th, so Range = 45/15 = 3
			var _tierRange:Number = _pirateTotalSum / TIER_RANGE_FRACTION_OF_TOTAL;
			
			
			for (i = 0; i <= NUM_PAYOUT_TIERS; i++) {
				payoutTiers[i] = new Payout(i * _tierRange, NUM_PAYOUT_TIERS - i);
				//tier range from i to i+1, i+1 to i+2, etc... payout multiplier
				//trace("tier<=" + payoutTiers[i].GetTier() + " payout:" + payoutTiers[i].GetPayout());		
			}
			
		}
		
		//UpdateText()
		//This function is mainly just used for testing purposes since anything from
		//this function shouldn't actually be displayed.
		public function UpdateText():void 
		{
			txtLabel.text = "Round #" + (GetNumRounds());
			txtLabel.appendText("\nGame Time: " + (gameTime / 1000) + " seconds");
			txtLabel.appendText("\nMoney: " + totalMoney);
			txtLabel.appendText("\nBet: $" + currentBet);
			txtLabel.appendText("\n\nScore: " + gameRef.GetRoundScore());
			txtLabel.appendText(",  Last: ");
			if (GetLastScore() >= 0) {
				txtLabel.appendText(GetLastScore().toString());
			}
			txtLabel.appendText(",  Avg: ");
			if (GetAvgScore() >= 0) {
				txtLabel.appendText(GetAvgScore().toFixed(1).toString());
			}
			txtLabel.appendText("\n#Rolls: " + GetNumRolls());
			txtLabel.appendText(",  Avg: " );
			if (GetAvgNumRolls() >= 0) {
				txtLabel.appendText(GetAvgNumRolls().toFixed(1).toString());
			}
			if (GetAvgRoundTime() >= 0) {
				txtLabel.appendText("\nAvg RoundLen: " + (GetAvgRoundTime()/1000).toFixed(1).toString() + " secs");
			}
			if (GetPerfectCount() > 0) {
				txtLabel.appendText("\n\n" + GetPerfectCount() + " perfect scores!");
			}
		}
		
		//IncrBetSize()
		//adjusts the current bet for this round
		public function IncrBetSize():void
		{
			if ((currentBet + BET_INCREMENT <= totalMoney) &&						//make sure we have enough to bet
					(currentBet + BET_INCREMENT <= (MAX_BET_SIZE_PERCENT * totalMoney))) {
				currentBet += BET_INCREMENT;
			}
			//if (currentBet + BET_INCREMENT <= totalMoney) {			//no max bet size!
				//currentBet += BET_INCREMENT;
			//}
		}
		
		//DecrBetSize()
		//adjusts the current bet for this round
		public function DecrBetSize():void
		{
			if (currentBet - BET_INCREMENT >= (MIN_BET_SIZE_PERCENT * totalMoney)) {
				currentBet -= BET_INCREMENT;
			}
		}
		
		
		//accessor functions
		
		//GetCurrentBetSize()
		//accessor function to return how much is being bet this round
		public function GetCurrentBetSize():int { return currentBet;}
		public function GetTotalMoney():int { return totalMoney; }
		public function GetRoundLengthInMilliseconds():int { return (getTimer() - roundStartTime); }
		public function GetGameLengthInMilliseconds():Number { return gameTime + GetRoundLengthInMilliseconds(); }	//used to tell "gametime" during a round
		public function GetGameTime():Number { return gameTime; }													//used to tell "gametime" between rounds
		public function GetDataLastTrackedTime():int { return dataLastTrackedTime; }
		public function GetAchievements():Achievements { return achievements; }
		public function GetFullTrackedDataString():String {
			return achievements.GetAchDataAsString() + trackedDataString + achievements.GetDataAsString().concat("</data>");
		}
		
		public function GetLastScore():int {return achievements.lastScore;}
		public function GetAvgScore():Number {return achievements.avgScore;}
		public function GetNumRounds():int {return achievements.numRounds;}
		public function GetPerfectCount():int {return achievements.perfectCount;}
		public function GetNumRolls():int {return achievements.numRolls;}
		public function GetAvgNumRolls():Number {return achievements.avgNumRolls;}
		public function GetAvgRoundTime():Number {return achievements.avgRoundTime;}
		
	}

}

class Payout
{
	private var tier:int;
	private var payout:Number;
	public function Payout(t:int, p:Number) {
		tier = t;
		payout = p;
	}
	public function GetTier():int {return tier;}			//$-value for the payout tier
	public function GetPayout():Number {return payout;}		//payout multiplier
	
}
